package com.kma.ncpractice2013.servlets;

import com.kma.ncpractice2013.auth.Crypto;
import com.kma.ncpractice2013.dao.*;
import com.kma.ncpractice2013.facade.MailFacade;
import com.kma.ncpractice2013.facade.MailFacadeImpl;
import com.kma.ncpractice2013.model.*;
import org.json.JSONArray;
import org.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Date;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * User: Viktor
 * Date: 11/26/13
 */
@WebServlet(name = "HotwireServlet")
public class HotwireServlet extends HttpServlet
{
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{

		String action = request.getParameter("action");
		if (action.equals("makeQuery"))
		{
			//http://api.ean.com/ean-services/rs/hotel/v3/list?minorRev=5&apiKey=p9ycn9cxb2zp3k3gfvbf5aym&sig=0936b0b4080387cdc90bf1507a869c27&cid=55505&locale=en_US
			// &city=Dallas&stateProvinceCode=TX&countryCode=US&numberOfResults=10&arrivalDate=2/7/2014&departureDate=2/9/2014&sort=DEFAULT&callback=displayHotelList&_type=json

			String query_url = "http://api.ean.com/ean-services/rs/hotel/v3/list?minorRev=5&apiKey=5zht8nshe8qhmwtnxxckqedy&sig=1730f8ec29bacc8f6c79f8a3cde8acd6&cid=55505&locale=en_US";
			query_url += ("&city=" + request.getParameter("dest"));
			query_url += ("&stateProvinceCode=&countryCode=&numberOfResults=20");
			query_url += ("&arrivalDate=" + request.getParameter("checkin"));
			query_url += ("&departureDate=" + request.getParameter("checkout"));
			JSONArray arr = new JSONArray(request.getParameter("roomArray"));
			int rooms = arr.length();
			int adults = 0;
			int adults_total = 0;
			int children = 0;
			int children_total = 0;
			for (int i = 0; i < rooms; i++)
			{
				adults = arr.getJSONArray(i).getInt(0);
				adults_total += adults;
				children = arr.getJSONArray(i).getInt(1);
				children_total += children;
				query_url += ("&room" + String.valueOf(i + 1) + "=" + (adults + children));
				if (children > 0)
				{
					for (int j = 0; j < children; j++)
						query_url += ",17";
				}
			}
			query_url += "&sort=DEFAULT&callback=displayHotelList&_type=json";
			URL url = new URL(query_url);
			URLConnection connection = url.openConnection();
			InputStream in = connection.getInputStream();
			String s = convertStreamToString(in);
			s = s.substring(17, s.length() - 2);
			HttpSession session = request.getSession(true);
			if (session.getAttribute("auth") == null)
			{
				session.setAttribute("auth", "false");
			}
			session.setAttribute("last_query", s);
			session.setAttribute("adults", adults_total);
			session.setAttribute("children", children_total);
			session.setAttribute("rooms", rooms);

			DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
			java.util.Date arrival  = null;
			java.util.Date departure = null;
			int nights = 0;
			try
			{
				arrival = formatter.parse(request.getParameter("checkin"));
				departure = formatter.parse(request.getParameter("checkout"));
				nights = departure.getDay() - arrival.getDay();
			} catch (ParseException e)
			{
				response.sendRedirect("/error.jsp?err=techDate");
			}
			session.setAttribute("checkin", arrival.getTime());
			session.setAttribute("checkout", departure.getTime());
			session.setAttribute("nights", nights);
			response.getOutputStream().print(s);

		}
		else if (action.equals("makeOrder"))
		{

			int hotel_id = Integer.parseInt(request.getParameter("hotelId"));

			HttpSession session = request.getSession(false);
			String promocode = request.getParameter("promocode");
			//double promoValue = Double.parseDouble(promocode);
			String payment_method = request.getParameter("paymentMethod");
			String jsonString = (String) session.getAttribute("last_query");
			int adults = (Integer)session.getAttribute("adults");
			int children = (Integer)session.getAttribute("children");
			int rooms = (Integer)session.getAttribute("rooms");
			int nights = (Integer)session.getAttribute("nights");
			session.setAttribute("last_query", null);
			UserDAO userDAO = new UserDAO();
			User user = null;
			if (session == null)
			{
				//error redirect
				response.getOutputStream().print("error?err=sessErr");
				return;
			}
			if (session.getAttribute("auth").equals("true") )
			{
				user = (User)session.getAttribute("user");
			}else
			if (session.getAttribute("auth").equals("false") )
			{
				String login = request.getParameter("username");
				if (login==null || !login.matches(".*[@].+"))
				{
					response.sendRedirect(response.encodeRedirectURL("/error.jsp?text=Please enter username"));
					return;
				}
				user = userDAO.getByLogin(login);
				if (user == null) //create user
				{

					String salt = Crypto.generateSalt();

					User new_user = new User();
					new_user.setLogin(login);

					new_user.setName(login);

					new_user.setAccessLevelId(User.AccessLevel.nonRegisteredCustomer);
					new_user.setGroupId(User.Group.nonRegisteredCustomer);
					new_user.setSalt(salt);
					new_user.setPassword(Crypto.getHash(salt, "password", "SHA-256"));
					new_user.setRegistrationToken(UUID.randomUUID().toString());
					new_user.setRegistrationDate(new Date(System.currentTimeMillis()));
					new_user.setBlocked(0);
					userDAO.create(new_user);
					user = userDAO.getByLogin(login);
				}
				else
				if (user.getGroupId() != User.Group.nonRegisteredCustomer)
				{
					response.sendRedirect(response.encodeRedirectURL("/error.jsp?text=Please log in"));
					return;
				}
			}



			JSONObject jsonObject = new JSONObject(jsonString);
			/*JSONArray hotel_list = jsonObject.getJSONObject("hotelListResponse").getJSONObject("HotelList").getJSONArray("HotelListSummary");
			JSONObject hotel = jsonArraySearch(hotel_list, "ResultId", hotel_id);
			JSONArray location_list = jsonObject.getJSONObject("MetaData").getJSONObject("HotelMetaData").getJSONArray("Neighborhoods");
			JSONObject neighborhood = jsonArraySearch(location_list, "Id", hotel.getString("NeighborhoodId"));*/
			JSONObject hotel = jsonArraySearch(jsonObject.getJSONObject("HotelListResponse").getJSONObject("HotelList").getJSONArray("HotelSummary"), "hotelId", hotel_id);



			String token = UUID.randomUUID().toString();
			java.sql.Date d = new java.sql.Date(System.currentTimeMillis());



			UserGroupDAO userGroupDAO = new UserGroupDAO();
			UserGroup userGroup = userGroupDAO.getById(user.getGroupId().getValue());

			int promoValue=0;
			if (promocode.length()==8)
			{
			PromoCodeDAO dao = new PromoCodeDAO();
			PromoCode code = dao.getByCode(promocode);
			promoValue = code.getDiscount();
			if (code==null || code.getUsed()==1 || code.getStartDate().after(d) || code.getExpirationDate().before(d) )
			{
				response.sendRedirect(response.encodeRedirectURL("/error.jsp?text=Invalid code"));
				return;
			}
				code.setUsed(1);
				dao.update(code);
			}
			double discount =  (userGroup.getDiscount() + promoValue) / 100.0;
			discount = Math.min(discount, 0.33);
			double commission =  Commission.getValue() / 100.0;
			double price = Double.parseDouble(hotel.getJSONObject("RoomRateDetailsList").getJSONObject("RoomRateDetails").getJSONObject("RateInfo").getJSONObject("ChargeableRateInfo").getString("@total"));
			price = price * (1.0 + commission * (1.0 - discount));
			price = Math.round(price * 100.0) / 100.0;

			double userBonus = user.getBonus();
			double totalBonus = user.getTotalBonus();
			double newBonus = 0;
			if (payment_method.equals("bonus"))
			{
				if (userBonus>price)
				{
					user.setBonus(userBonus-price);
					userDAO.update(user);
				}
				else
				{
					response.sendRedirect(response.encodeRedirectURL("/error.jsp?text=Not enough bonus points"));

					return;
				}
			}
			else
			{
				if(user.getGroupId() == User.Group.ordinaryCustomer)
				{
					newBonus = price * 0.01;
					user.setBonus(userBonus+newBonus);
					user.setTotalBonus(totalBonus+newBonus);
					userDAO.update(user);
				}
				else if (user.getGroupId() == User.Group.VIPCustomer)
				{
					newBonus = price * 0.03;
					user.setBonus(userBonus+newBonus);
					user.setTotalBonus(totalBonus+newBonus);
					userDAO.update(user);
				}
			}




			HotelDAO hotelDAO = new HotelDAO();
			Hotel h = hotelDAO.getByHotelId(hotel_id);
			if (h==null)
			{
				h = new Hotel();
				h.setHotelId(hotel_id);
				h.setLongitude(hotel.getDouble("longitude"));
				h.setName(hotel.getString("name"));
				h.setLatitude(hotel.getDouble("latitude"));
				h.setAddress(hotel.getString("address1"));
				h.setCity(hotel.getString("city"));
				h.setDescription(hotel.getString("locationDescription"));
				h.setCountry(hotel.getString("countryCode"));
				hotelDAO.create(h);
			}
			OrderDAO orderDAO = new OrderDAO();
			Order order = new Order();
			order.setLocationId(hotel_id);
			order.setRooms(rooms);
			order.setUserId(user.getId());
			order.setConfirmed(Order.NonConfirmed);
			order.setNights(nights);
			order.setOrderDate(d);
			order.setAdults(adults);
			order.setChildren(children);
			order.setHotelStars(hotel.getInt("hotelRating"));
			order.setConfirmationToken(token);
			order.setBonusGained(newBonus);
			//DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
			Date checkInDate = new Date((Long)session.getAttribute("checkin"));
			Date checkOutDate = new Date((Long)session.getAttribute("checkout"));
			/*try
			{
				checkInDate =  new Date(formatter.parse(hotel.getString("CheckInDate")).getTime());
				checkOutDate =  new Date(formatter.parse(hotel.getString("CheckOutDate")).getTime());
			} catch (ParseException e)
			{
				response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/error.jsp"));
			}*/

			order.setCheckInDate(checkInDate);
			order.setCheckOutDate(checkOutDate);
			order.setPrice(price);
			order.setAmenityMask(hotel.getInt("amenityMask"));
			//order.setLocationId(location.getId());
			orderDAO.create(order);



			MailFacade mail = new MailFacadeImpl();
			String subject = "Reservation System order confirmation";
			String letter =         "\nYour order is: " +
					                "\nHotel" + h.getCountry()+" "+h.getCity()+" "+h.getName()+
									"\n"+h.getDescription()+
									"\nPrice: $"+order.getPrice()+
									"\nNights: "+nights+
							        "Click on the following link to confirm your order.\n" +
					                MailFacadeImpl.home + "/orderconfirmation.jsp?token=" + token;

			try
			{
				mail.sendLetter(user.getLogin(), subject, letter);

			} catch (Exception e)
			{


				response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/error.jsp?err=mailErr&text="+e.getMessage()));
				return;
			}
			session.setAttribute("user", user);
			response.sendRedirect(response.encodeRedirectURL("/success.jsp?text=hotel ordering successful\n" +
					                                                 "payment method is " + payment_method + "\nyou get +" + newBonus + " bonus points"));


		}

	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{

	}
	private String convertStreamToString(java.io.InputStream is) {
		java.util.Scanner s = new java.util.Scanner(is, "UTF-8").useDelimiter("\\A");
		return s.hasNext() ? s.next() : "";
	}
	private JSONObject jsonArraySearch(JSONArray array, String param, int target)
	{
		for (int i = 0; i < array.length(); i++)
		{
			if (array.getJSONObject(i).getInt(param) == target)
			{
				return array.getJSONObject(i);
			}
		}
		return null;
	}

}
